// ProgressDialog2.h

#ifndef __PROGRESS_DIALOG2_H
#define __PROGRESS_DIALOG2_H

#include "Common/System/Threading/Utility.h"
#include "Common/System/Threading/Thread.h"

#include "Common/MyFixes.h"

#include "resource.h"


#include "Common/System/Threading/SimpleWorkerPool.h"
#include <memory>

static const UINT kCloseMessage = WM_USER + 1;

class CProgressSync
{
	bool _stopped;
	bool _paused;
	bool _bytesProgressMode;

	UINT64 _totalBytes;
	UINT64 _curBytes;
	UINT64 _totalFiles;
	UINT64 _curFiles;
	UINT64 _inSize;
	UINT64 _outSize;

	CString _titleFileName;
	CString _currentFileName;

public:
	CStringVector Messages;
	CString ErrorMessage;
	CString ErrorMessageTitle;

	CString OkMessage;
	CString OkMessageTitle;


	CComAutoCriticalSection _cs;

	CProgressSync():
		_stopped(false), _paused(false),
		_totalBytes((UINT64)(INT64) - 1), _curBytes(0),
		_totalFiles((UINT64)(INT64) - 1), _curFiles(0),
		_inSize((UINT64)(INT64) - 1),
		_outSize((UINT64)(INT64) - 1),
		_bytesProgressMode(true)
	{}

	bool GetStopped()
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		return _stopped;
	}
	void SetStopped(bool value)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_stopped = value;
	}
	bool GetPaused()
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		return _paused;
	}
	void SetPaused(bool value)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_paused = value;
	}
	void SetBytesProgressMode(bool bytesProgressMode)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_bytesProgressMode = bytesProgressMode;
	}
	void SetProgress(UINT64 total, UINT64 completed)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_totalBytes = total;
		_curBytes = completed;
	}
	void SetRatioInfo(const UINT64* inSize, const UINT64* outSize)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		if (inSize)
		{
			_inSize = *inSize;
		}
		if (outSize)
		{
			_outSize = *outSize;
		}
	}
	void SetPos(UINT64 completed)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_curBytes = completed;
	}
	void SetNumBytesTotal(UINT64 value)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_totalBytes = value;
	}
	void SetNumFilesTotal(UINT64 value)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_totalFiles = value;
	}
	void SetNumFilesCur(UINT64 value)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_curFiles = value;
	}
	HRESULT ProcessStopAndPause();
	HRESULT SetPosAndCheckPaused(UINT64 completed);
	void GetProgress(UINT64 &total, UINT64 &completed,
					 UINT64 &totalFiles, UINT64 &curFiles,
					 UINT64 &inSize, UINT64 &outSize,
					 bool &bytesProgressMode)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		total = _totalBytes;
		completed = _curBytes;
		totalFiles = _totalFiles;
		curFiles = _curFiles;
		inSize = _inSize;
		outSize = _outSize;
		bytesProgressMode = _bytesProgressMode;
	}
	void SetTitleFileName(const CString &fileName)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_titleFileName = fileName;
	}
	void GetTitleFileName(CString &fileName)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		fileName = _titleFileName;
	}
	void SetCurrentFileName(const CString &fileName)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		_currentFileName = fileName;
	}
	void GetCurrentFileName(CString &fileName)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		fileName = _currentFileName;
	}

	void AddErrorMessage(LPCWSTR message)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		Messages.push_back(message);
	}

	void SetErrorMessage(const CString &message)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		ErrorMessage = message;
	}

	void SetOkMessage(const CString &message)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		OkMessage = message;
	}

	void SetOkMessageTitle(const CString &title)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		OkMessageTitle = title;
	}

	void SetErrorMessageTitle(const CString &title)
	{
		Common::System::Threading::CComScopeAutoLock lock(_cs);
		ErrorMessageTitle = title;
	}

	bool ThereIsMessage() const
	{
		return !Messages.empty() || !ErrorMessage.IsEmpty() || !OkMessage.IsEmpty();
	}
};

class CU64ToI32Converter
{
	UINT64 _numShiftBits;
public:
	void Init(UINT64 range)
	{
		// Windows CE doesn't like big number here.
		for (_numShiftBits = 0; range > (1 << 15); _numShiftBits++)
		{
			range >>= 1;
		}
	}
	int Count(UINT64 value)
	{
		return int(value >> _numShiftBits);
	}
};

enum ESpeedMode
{
	kSpeedBytes,
	kSpeedKBytes,
	kSpeedMBytes
};

class CProgressDialog2: public CDialogImpl<CProgressDialog2>
{
public:
	enum { IDD = IDD_DIALOG_PROGRESS};

	BEGIN_MSG_MAP(CProgressDialog2)
	MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
	MESSAGE_HANDLER(WM_SIZE, OnSize)
	MESSAGE_HANDLER(WM_TIMER, OnTimer)
	MESSAGE_HANDLER(kCloseMessage, OnCloseMsg)
	COMMAND_ID_HANDLER(IDOK, OnOK)
	COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
	COMMAND_ID_HANDLER(IDC_BUTTON_PAUSE, OnCancel)
	COMMAND_ID_HANDLER(IDC_BUTTON_PROGRESS_PRIORITY, OnCancel)
	END_MSG_MAP()
	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL & /*bHandled*/);
	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL & /*bHandled*/);
	LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL &bHandled);
	LRESULT OnPauseButton(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL & /*bHandled*/);
	LRESULT OnPriorityButton(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL & /*bHandled*/);
	LRESULT OnCloseMsg(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL & /*bHandled*/);

	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL & /*bHandled*/)
	{
		Sync.SetStopped(true);
		EndDialog(wID);
		return 0;
	}

	CString _prevFileName;
	CString _prevTitleName;
private:
	CString backgroundString;
	CString backgroundedString;
	CString foregroundString;
	CString pauseString;
	CString continueString;
	CString pausedString;

	int buttonSizeX;
	int buttonSizeY;

	UINT_PTR _timer;

	CString _title;
	CU64ToI32Converter _converter;
	UINT64 _previousPos;
	UINT64 _range;
	CProgressBarCtrl m_ProgressBar;
	CListViewCtrl _messageList;

	UINT32 _prevPercentValue;
	UINT32 _prevTime;
	UINT32 _elapsedTime;
	UINT32 _prevElapsedSec;
	UINT64 _prevRemainingSec;
	ESpeedMode _prevMode;
	UINT64 _prevSpeed;

	bool _foreground;

	int _numReduceSymbols;

	bool _wasCreated;
	bool _needClose;

	UINT32 _numPostedMessages;
	UINT32 _numAutoSizeMessages;

	bool _errorsWereDisplayed;

	bool _waitCloseByCancelButton;
	bool _cancelWasPressed;

	bool _inCancelMessageBox;
	bool _externalCloseMessageWasReceived;

	//Currently all dialogs execute in different thread
	//and main thread waits
	//TODO: change this behaviour
	Common::System::Threading::CEvent _ThreadAutoFinish;


	void UpdateStatInfo(bool showAll);
	LRESULT OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL & /*bHandled*/);
	void SetRange(UINT64 range);
	void SetPos(UINT64 pos);
	Common::System::Threading::CEvent _createDialogEvent;
	Common::System::Threading::CEvent _dialogCreatedEvent;
#ifndef _SFX
	void AddToTitle(LPCTSTR string);
#endif

	void SetPauseText();
	void SetPriorityText();

	void SetTitleText();
	void ShowSize(int id, UINT64 value);

	void UpdateMessagesDialog();

	void AddMessageDirect(LPCTSTR message);
	void AddMessage(LPCTSTR message);

	bool OnExternalCloseMessage();
	void EnableErrorsControls(bool enable);

	void ShowAfterMessages(HWND wndParent);

	void CheckNeedClose();
public:
	CProgressSync Sync;
	bool CompressingMode;
	bool WaitMode;
	bool ShowCompressionInfo;
	bool MessagesDisplayed; // = true if user pressed OK on all messages or there are no messages.
	int IconID;

#ifndef _SFX
	HWND MainWindow;
	CString MainTitle;
	CString MainAddTitle;
	~CProgressDialog2();
#endif

	CProgressDialog2();
	void WaitCreating()
	{
		_createDialogEvent.SetEvent();
		_dialogCreatedEvent.WaitForObject();
	}

	HANDLE GetThreadFinishHandle()
	{
		return _ThreadAutoFinish;
	}

	INT_PTR DoModalAndWait(const CString &title, HANDLE hEvent, HWND hWndParent = ::GetActiveWindow(),
						   LPARAM dwInitParam = NULL);
	void ProcessWasFinished();

	typedef CDialogImpl<CProgressDialog2> baseClass;


};


class CProgressCloser
{
	CProgressDialog2* _p;
public:
	CProgressCloser(CProgressDialog2 &p) : _p(&p) {}
	~CProgressCloser()
	{
		_p->ProcessWasFinished();
	}
};

class CProgressThreadVirt
{
public:
	typedef Common::System::Threading::ThreadPoolPtr ThreadPoolType;
protected:
	CString ErrorMessage;
	CString ErrorPath1;
	CString ErrorPath2;
	CString OkMessage;
	CString OkMessageTitle;
	ThreadPoolType ThreadPool;

	// error if any of HRESULT, ErrorMessage, ErrorPath
	virtual HRESULT ProcessVirt() = 0;
	void Process();
public:
	HRESULT Result;
	bool ThreadFinishedOK; // if there is no fatal exception
	CProgressDialog2 ProgressDialog;

	static void WINAPI MyThreadFunction(LPVOID param);


	HRESULT Create(const CString &title, HWND parentWindow = 0);
	CProgressThreadVirt(ThreadPoolType* pool):
		Result(E_FAIL),
		ThreadFinishedOK(false),
		ThreadPool(*pool)
	{}
};

CString HResultToMessage(HRESULT errorCode);

#endif
